package com.ming_za.od_updater_sdk.processing.record;

import com.ming_za.od_updater_sdk.IUpdater;
import com.ming_za.od_updater_sdk.exception.*;
import com.ming_za.od_updater_sdk.obj.UpdateRecord;
import com.ming_za.od_updater_sdk.utils.BinaryUtils;
import com.ming_za.od_updater_sdk.utils.VersionUtils;

import java.io.*;
import java.nio.file.Files;
import java.util.HashSet;
import java.util.Set;

/**
 * 默认更新记录器
 * <pre>
 * 数据记录格式：
 * 4byte - 最后一次 已更新完成的版本号
 * 4byte - 最后一次 未更新完成的版本号
 * 8byte - 最后一次版本已更新完成的资源id
 * ... - 往后每8byte记录一次已完成更新的资源id
 * </pre>
 */
public class DefaultUpdateRecorder implements IUpdateRecorder {

    /**
     * 文件名
     */
    private final String fileName = "version.odu";

    /**
     * 指定版本后的文件输出流
     */
    private OutputStream outputStream = null;
    /**
     * 当前指定的版本号
     */
    private int nowVersion = 0;

    @Override
    public UpdateRecord getUpdatedVersion(IUpdater odUpdater) {
        File file = new File(odUpdater.getRootDirectory(),fileName);

        if (!file.exists()) {
            //没有文件记录
            return new UpdateRecord(0,0,new HashSet<>());
        }

        try {
            InputStream inputStream = Files.newInputStream(file.toPath());

            byte[] header = null;
            Set<Long> resIds = new HashSet<>();

            byte[] buffer = new byte[8];
            int len;
            while ((len = inputStream.read(buffer)) != -1){

                if (len != 8){
                    continue;
                }

                //读取文件数据头
                if (header == null){
                    header = new byte[8];
                    System.arraycopy(buffer,0,header,0,8);
                    continue;
                }

                //读取已完成的资源id
                resIds.add(BinaryUtils.byteArrayToLong(buffer));
            }

            inputStream.close();

            if (header != null){
                int version = BinaryUtils.byteArrayToInt(header);
                int notCompletedVersion = BinaryUtils.byteArrayToInt(new byte[]{header[4], header[5], header[6], header[7]});
                return new UpdateRecord(version,notCompletedVersion,resIds);
            }else {
                return new UpdateRecord(0,0,new HashSet<>());
            }
        } catch (IOException e) {
            throw new FileReadException("文件读取出错！",e);
        }
    }

    /**
     * 当前的版本文件
     */
    private File versionFile = null;

    private IUpdater odUpdater = null;

    @Override
    public void useVersion(IUpdater odUpdater, int version) {
        if (isUseVersion()){
            throw new ExistingVersionUseException("当前已存在指定版本，不能再次指定，请先调用complete()或closeStream()结束当前版本的操作");
        }

        this.odUpdater = odUpdater;

        versionFile = new File(odUpdater.getRootDirectory(),fileName);

        try {
            if (versionFile.exists()) {

                odUpdater.getLogger().debug("加载本地记录数据头");

                InputStream inputStream = Files.newInputStream(versionFile.toPath());

                //文件头
                byte[] header = new byte[8];

                if (inputStream.read(header) < 8){
                    inputStream.close();
                    throw new FileTypeException("文件类型异常");
                }
                inputStream.close();

                //未完成更新的版本
                int unCompleteVersion = BinaryUtils.byteArrayToInt(new byte[]{header[4], header[5], header[6], header[7]});

                /*
                    上次更新未完成的版本是否与当前指定的版本相同
                    没有未完成的版本或者未完成的版本与指定版本不同时 - 覆盖上次记录的资源id
                    存在未完成版本，而且版本与当前指定的版本相同时 - 续上次更新累加资源id
                */
                if (unCompleteVersion != version){
                    odUpdater.getLogger().debug("可能不存在未完成更新的版本->覆盖旧数据");
                    BinaryUtils.insertInt(header,4,version);
                    outputStream = new FileOutputStream(versionFile,false);
                    outputStream.write(header);
                }else {
                    outputStream = new FileOutputStream(versionFile,true);
                }
            }else {
                odUpdater.getLogger().debug("未找到版本记录文件-->新建文件");

                if (!versionFile.getParentFile().exists()){
                    if (!versionFile.getParentFile().mkdirs()){
                        throw new FileReadException("无法创建父级文件夹！");
                    }
                }

                outputStream = Files.newOutputStream(versionFile.toPath());

                //初始化当前版本状态，最后一次完成更新的版本号设为0，未完成更新的版本号设为当前正在更新的版本
                byte[] header = new byte[8];
                BinaryUtils.insertInt(header, 4, version);
                outputStream.write(header);
            }
        }catch (IOException e) {
            throw new FileReadException("文件读取出错！",e);
        }

        nowVersion = version;
    }

    /**
     * 该方法不检测是否已经存在相同的resId，请在调用该方法前自行判断是否需要记录
     */
    @Override
    public void addRes(long resId) {
        if (!isUseVersion()){
            throw new VersionUnUseException("没有指定版本");
        }

        try {
            //累加资源id
            odUpdater.getLogger().debug("记录更新-->资源id:" + resId);
            outputStream.write(BinaryUtils.longToByteArray(resId));
            tryFlush();
        } catch (IOException e) {
            throw new FileWriteException("文件写入出错！",e);
        }
    }

    long lastFlush = 0;

    void tryFlush() throws IOException {
        long now = System.currentTimeMillis();
        if (now - lastFlush > 1000){
            lastFlush = now;
            outputStream.flush();
        }
    }

    @Override
    public void complete() {

        if (isUseVersion()){
            try {
                outputStream.close();

                byte[] header = new byte[8];
                BinaryUtils.insertInt(header,0,nowVersion);

                /*
                重写更新文件数据
                文件头：
                    最后一次更新完成的版本 = 当前指定的版本
                    未完成的版本 = 0
                资源id:无数据
                */
                OutputStream completeOutputStream = new FileOutputStream(versionFile,false);
                completeOutputStream.write(header);
                completeOutputStream.close();

                odUpdater.getLogger().debug("完成记录-->[v" + VersionUtils.intToVersion(nowVersion) + "]");
            } catch (IOException ignored) {
            }
            nowVersion = 0;
        }else {
            throw new VersionUnUseException("没有指定版本");
        }
    }

    @Override
    public void closeStream(){
        if (outputStream != null){
            try {
                odUpdater.getLogger().debug("中断记录-->[v" + VersionUtils.intToVersion(nowVersion) + "]");
                outputStream.close();
            } catch (IOException ignored) {
            }
            nowVersion = 0;
        }
    }

    @Override
    public boolean isUseVersion() {
        return nowVersion != 0;
    }

}
